"use strict";

const self = this;

var precacheConfig = [["index.html", new Date().getTime()]];

const createCacheKey = function (
  originalUrl,
  paramName,
  paramValue,
  dontCacheBustUrlsMatching
) {
  // Create a new URL object to avoid modifying originalUrl.
  var url = new URL(originalUrl);

  // If dontCacheBustUrlsMatching is not set, or if we don't have a match,
  // then add in the extra cache-busting URL parameter.
  if (
    !dontCacheBustUrlsMatching ||
    !url.pathname.match(dontCacheBustUrlsMatching)
  ) {
    url.search +=
      (url.search ? "&" : "") +
      encodeURIComponent(paramName) +
      "=" +
      encodeURIComponent(paramValue);
  }

  return url.toString();
};

const cleanResponse = function (originalResponse) {
  // If this is not a redirected response, then we don't have to do anything.
  if (!originalResponse.redirected) {
    return Promise.resolve(originalResponse);
  }

  // Firefox 50 and below doesn't support the Response.body stream, so we may
  // need to read the entire body to memory as a Blob.
  var bodyPromise =
    "body" in originalResponse
      ? Promise.resolve(originalResponse.body)
      : originalResponse.blob();

  return bodyPromise.then(function (body) {
    // new Response() is happy when passed either a stream or a Blob.
    return new Response(body, {
      headers: originalResponse.headers,
      status: originalResponse.status,
      statusText: originalResponse.statusText,
    });
  });
};

var cacheName =
  "sw-precache" + (self.registration ? self.registration.scope : "");

var hashParamName = "_sw-precache";
var urlsToCacheKeys = new Map(
  precacheConfig.map(function (item) {
    var relativeUrl = item[0];
    var hash = item[1];
    var absoluteUrl = new URL(relativeUrl, self.location);
    var cacheKey = createCacheKey(absoluteUrl, hashParamName, hash);
    return [absoluteUrl.toString(), cacheKey];
  })
);

function setOfCachedUrls(cache) {
  return cache
    .keys()
    .then(function (requests) {
      return requests.map(function (request) {
        return request.url;
      });
    })
    .then(function (urls) {
      return new Set(urls);
    });
}

const update = (callback) => {
  caches
    .open(cacheName)
    .then(function (cache) {
      return setOfCachedUrls(cache).then(function (cachedUrls) {
        return Promise.all(
          Array.from(urlsToCacheKeys.values()).map(function (cacheKey) {
            // If we don't have a key matching url in the cache already, add it.
            if (!cachedUrls.has(cacheKey)) {
              var request = new Request(cacheKey, {
                credentials: "same-origin",
              });
              return fetch(request).then(function (response) {
                // Bail out of installation unless we get back a 200 OK for
                // every request.
                if (!response.ok) {
                  throw new Error(
                    "Request for " +
                      cacheKey +
                      " returned a " +
                      "response with status " +
                      response.status
                  );
                }

                return cleanResponse(response).then(function (responseToCache) {
                  return cache.put(cacheKey, responseToCache);
                });
              });
            }
          })
        );
      });
    })
    .then(function () {
      typeof callback === "function" && callback();
      // Force the SW to transition from installing -> active state
      return self.skipWaiting();
    })
    .catch((err) => console.error(err));
};

self.addEventListener("install", function (event) {
  event.waitUntil(update());
});

self.addEventListener("activate", function (event) {
  var setOfExpectedUrls = new Set(urlsToCacheKeys.values());

  event.waitUntil(
    caches
      .open(cacheName)
      .then(function (cache) {
        return cache.keys().then(function (existingRequests) {
          return Promise.all(
            existingRequests.map(function (existingRequest) {
              if (!setOfExpectedUrls.has(existingRequest.url)) {
                return cache.delete(existingRequest);
              }
            })
          );
        });
      })
      .then(function () {
        return self.clients.claim();
      })
  );
});

self.addEventListener("fetch", function (event) {
  // console.log(event.request.url, caches);
  event.respondWith(
    caches.match(event.request).then((res) => {
      return (
        res ||
        fetch(event.request)
          .then((response) => {
            if (!response.ok) {
              throw new Error(
                "Request for " +
                  event.request +
                  " returned a " +
                  "response with status " +
                  response.status
              );
            }

            const responseClone = response.clone();
            caches.open(cacheName).then((cache) => {
              cache.put(event.request, responseClone);
            });

            return response;
          })
          .catch((err) => console.error(err))
      );
    })
  );
});

self.onmessage = function (e) {
  switch (e.data) {
    case "sw.updateDone":
      e.waitUntil(
        update(() => {
          self.clients.matchAll().then(function (clients) {
            if (clients && clients.length) {
              clients.forEach(function (client) {
                client.postMessage(e.data);
              });
            }
          });
        })
      );
      break;
  }
};
